package level;

import flixel.FlxObject;
import flixel.FlxBasic;
import flixel.FlxSprite;
import flixel.FlxG;
import flixel.util.FlxPoint;
import flixel.group.FlxGroup;
import flixel.group.FlxTypedGroup;
import flixel.addons.tile.FlxTilemapExt;
import level.RobotSpawner;
import level.Computer;
import utils.TiledMapLoader;
import utils.CameraController;
import items.Diskette;
import sensors.BasicSensor;
import sensors.TouchSensor;
import sensors.AndSensor;
import sensors.DoorSensor;
//////////////////////////////////////////////////////////////////
/// \class: Level
/// \brief: 


///	Capas del Nivel
/// * background: fondo, computers, robotSpawners
/// * solid: contra lo que se puede colisionar
/// * foreground: sobre solid pero debajo del personaje: wires, sensors 
/// * foreground2: cosas que esten por encima del character: wires, cosas raras
/// * entities: entidades: sensors, entrypoints, computers, robotSpawners
//////////////////////////////////////////////////////////////////
class Level extends FlxBasic
{
	public var solidLayer: 	FlxTilemapExt;
	public var backgroundLayer: FlxTilemapExt;
	public var parallaxBackgroundLayer: FlxTilemapExt;
	public var foregroundLayer: FlxTilemapExt;

	private var characterGroup: FlxGroup;
	public var character: Character;
	private var _cameraController: CameraController;

	/// GRUPOS DE ENTIDADES EN EL NIVEL ////
	public var solidEntities: 		FlxGroup;	// entidades contra las que el jugador puede colisionar
	public var overlapableEntities: FlxGroup;	// entidades contra las que el jugador debe testear solapamiento
	public var updatableEntities: 	FlxGroup;	// entidades actualizables y dibujables que están a la misma altura que la capa solid (antes del jugador)
	public var drawableEntities: 	FlxGroup;	// entidades que se dibujan
	
	

	public function new(_Level: String)
	{
		super();
		trace("Level:: Loading level: "+_Level);
		
		// limpia los arreglos estaticos
		Computer.clearAll();
		RobotSpawner.clearAll();
		Robot.clearAll();
		EntryPoint.clearAll();
		Cannon.clearAll();

		//
		BasicSensor.initAll();
		
		// crea los grupos de entidades
		solidEntities 		= new FlxGroup();
		overlapableEntities = new FlxGroup();
		updatableEntities 	= new FlxGroup();
		drawableEntities 	= new FlxGroup();

		var loader = new TiledMapLoader("assets/levels/" + _Level);
		
		// carga las capas de tiles
		parallaxBackgroundLayer = loader.loadPatternLayer("parallax_background", "assets/img/megacommando.png");
		parallaxBackgroundLayer.scrollFactor.x = 0.8;
		backgroundLayer = loader.loadPatternLayer("background", "assets/img/megacommando.png");
		solidLayer 		= loader.loadPatternLayer("solid", 		"assets/img/megacommando.png");
		foregroundLayer = loader.loadPatternLayer("foreground", "assets/img/megacommando.png");

		// carga los tiles de tipo "nubes"
		for( i in 0...Tiles.CLOUD_TILES.length)
		{
			solidLayer.setTileProperties(Tiles.CLOUD_TILES[i], FlxObject.UP);
		}

		// carga los tiles contra los que no queremos colisionar
		for( i in 0...Tiles.BACKGROUND_TILES.length)
		{
			solidLayer.setTileProperties(Tiles.BACKGROUND_TILES[i], FlxObject.NONE);
		}
		// 
		solidLayer.setSlopes([829], [830], [], []);

		// carga las entidades representadas por tiles
		loadTileEntities();
		// carga los sensores
		loadSensors(foregroundLayer);
		// una vez que los sensores fueron cargados, los podemos inicializar
		BasicSensor.bindAll();
		
		new MessageBoard();

		_cameraController = new CameraController();
		trace("Loading entities...");
		loader.loadObjectGroup("entities", objectLoadCallback);

		// crea y posiciona al personaje
		// tenemos que hacerlo después de cargar las entrypoints
		var entryPoint = EntryPoint.findEntryPoint(Reg.entryPointId);
		character = new Character(entryPoint.x, entryPoint.y, solidLayer);		
		characterGroup = new FlxGroup();
		characterGroup.add(character);
		// arregla la posicion del character en y
		character.y = entryPoint.y + (64-45); // altura del EP - altura del personaje
		character.flipX = entryPoint.facingLeft == 1;
		_cameraController.setTarget(character);

		FlxG.camera.setBounds(0, 0, loader.totalWidth, loader.totalHeight, true);

		// agrega el dev panel
		var devPanel = DeveloperPanel.getInstance();
		devPanel.initDevPanel();
		devPanel.visible = false;

		// agrega las entidades a sus correspondientes grupos
		solidEntities.add(solidLayer);
		overlapableEntities.add(Computer._computers);
		overlapableEntities.add(BasicSensor.getSensors("TouchSensor"));
		overlapableEntities.add(MessageRegion.messageRegions);
		overlapableEntities.add(Cannon.getBullets());

		//drawableEntities.add(parallaxBackgroundLayer);
		drawableEntities.add(backgroundLayer);
		drawableEntities.add(solidLayer);
		drawableEntities.add(RobotSpawner.getRobotSpawners());
		drawableEntities.add(foregroundLayer);
		drawableEntities.add(Robot.getRobots());
		drawableEntities.add(Cannon.getBullets());
		drawableEntities.add(Cannon.getCannons());
		drawableEntities.add(characterGroup);
		drawableEntities.add(MessageBoard.instance);
		drawableEntities.add(devPanel);
		// sacar esto
		drawableEntities.add(BasicSensor.getSensors("TouchSensor"));
		//drawableEntities.add(devPanel);
		
		updatableEntities.add(characterGroup);
		updatableEntities.add(RobotSpawner.getRobotSpawners());
		updatableEntities.add(Robot.getRobots());
		updatableEntities.add(devPanel);
		updatableEntities.add(BasicSensor.getSensors("TouchSensor"));
		updatableEntities.add(BasicSensor.getSensors("DoorSensor"));
		updatableEntities.add(BasicSensor.getSensors("AndSensor"));
		updatableEntities.add(_cameraController);
		updatableEntities.add(MessageBoard.instance);
		updatableEntities.add(Cannon.getCannons());
		updatableEntities.add(Cannon.getBullets());
	}


	private function objectLoadCallback(Obj: utils.TiledMapLoader.TiledObject){
		switch (Obj.type) {
			case "computer":
				var id = Obj.properties.getInt("id");
				var availableActions = Obj.properties.getString("availableActions");
				var computer = new Computer(Obj.x, Obj.y, id, availableActions);
				trace("Creating computer #"+id+" at ("+Obj.x+","+Obj.y+")");

			case "entrypoint":
				var id = Obj.properties.getInt("id");
				var facing = Obj.properties.getInt("facingleft");
				var entrypoint = new EntryPoint(Obj.x, Obj.y, id, facing);

			case "exitregion":
				var foreignMap: String = Obj.properties.getString("foreign_map");
				var foreignMapEntryPointId: Int = Obj.properties.getInt("foreign_id");
				var exitregion = new ExitRegion(Obj.x, Obj.y, Obj.width, Obj.height, foreignMap, foreignMapEntryPointId);
				overlapableEntities.add(exitregion);

			case "robotspawner":
				var id = Obj.properties.getInt("id");
				trace("Raeding RS id "+id);
				var robotSpawner = new RobotSpawner(Obj.x, Obj.y, id);

			case "messageregion":
				var msg: String = Obj.properties.getString("message");
				var _msgRegion = new MessageRegion(Obj.x, Obj.y, Std.int(Obj.width), Std.int(Obj.height), msg);

			case "diskette":
				var id: Int = Obj.properties.getInt("id");
				if(!Reg.hasItem("diskette_"+id))
				{
					var diskette = new Diskette(id, Obj.x, Obj.y);
					updatableEntities.add(diskette);
					overlapableEntities.add(diskette);
				}

			case "cameraregion":
				var region = new FlxObject(Obj.x, Obj.y);
				region.width = Obj.width;
				region.height = Obj.height;
				_cameraController.addCameraRegion(region);

			case "cannon":
				var cannontype = Obj.properties.getInt("cannontype");
				var cannon = new Cannon(Obj.x, Obj.y, cannontype);
				
			//case "spikes":
			//	overlapableEntities.add(new Spikes(Obj.x, Obj.y));
			
			default:
				//throw("Level::objectLoadCallback(): No se reconoce el tipo de entidad \""+Obj.type+"\"");
		}
	}





	public function onOverlapRobotSensor(Robot: FlxBasic, TheSensor: FlxBasic)
	{
		var sensor = cast(TheSensor, BasicSensor);
		sensor.activate();
	}

	/// esta función esta hecha para meter, más adelante, código que se deberá ejecutar al tocar una compu
	public function showComputerScreen(thecomputer: Computer)
	{
		//trace("show computer screen");
		var devPanel = DeveloperPanel.getInstance();
		devPanel.bringToFront(thecomputer.id);
	}

	/// Sobrecarga de la funcion de actualizado
	public override function update()
	{
		super.update();
		updatableEntities.update();
		FlxG.overlap(Robot.getRobots(), BasicSensor.getSensors("TouchSensor"), onOverlapRobotSensor);
	}

	/// Sobrecarga de la funcion de dibujado
	public override function draw(): Void
	{
		super.draw();
		drawableEntities.draw();
	}

	private function loadSensors(sensorLayer: FlxTilemapExt)
	{
		// carga y crea los touch sensors
		var tiles = sensorLayer.getTileCoords(level.Tiles.TOUCH_SENSOR_OFF);
		if(tiles != null){
			for(tileCoords in tiles)
			{
				trace("Creating touch sensor");
				new TouchSensor(tileCoords.x, tileCoords.y, sensorLayer);
			}
		}

		// carga y crea los door sensors
		tiles = sensorLayer.getTileCoords(level.Tiles.DOOR_SENSOR_OFF);
		if(tiles != null){
			for(tileCoords in tiles)
			{
				trace("Creating door sensor");
				new DoorSensor(tileCoords.x, tileCoords.y, sensorLayer, solidLayer);
			}
		}

		// carga y crea los And sensors
		tiles = sensorLayer.getTileCoords(level.Tiles.AND_SENSOR_OFF);
		if(tiles != null){
			for(tileCoords in tiles)
			{
				//trace("Creating And sensor");
				new AndSensor(tileCoords.x, tileCoords.y, sensorLayer);
			}
		}
	}

	private function loadTileEntities()
	{
		// carga los spikes
		var spikes = new FlxGroup();
		var tiles = backgroundLayer.getTileCoords(level.Tiles.SPIKE_TILE);
		if(tiles != null){
			for(tileCoords in tiles)
			{
				//trace("Creating spike at: "+tileCoords.x+" "+tileCoords.y);
				spikes.add(new Spikes(tileCoords.x, tileCoords.y));
			}
		}
		overlapableEntities.add(spikes);
	}
}